﻿using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;
using System.Web;

namespace API.Models.Repository
{
    public interface IGenericRepository<T> where T : class
    {
        IQueryable<T> Find();
        IQueryable<T> Find(Expression<Func<T, bool>> predicate);
        IQueryable<T> Find(IEnumerable<string> includes, Expression<Func<T, bool>> predicate);
        void Add(T entity);
        void Delete(T entity);
        void Edit(T entity);
        void Save();
        void SetSession(SEC_SESSIONS currentSession);
    }

    //public class CommonRepository<T> : GenericRepository<PartnerDbContext, T> where T : class, ICommonDbClass { }

    public class GenericRepository<C, T> : IGenericRepository<T>
        where T : class, ICommonDbClass
        where C : DbContext, new()
    {
        public SEC_SESSIONS session { get; set; }

        public virtual void SetSession(SEC_SESSIONS currentSession)
        {
            this.session = currentSession;
        }

        public C _entities = new C();
        public C Context
        {
            get { return _entities; }
            set { _entities = value; }
        }

        public virtual IQueryable<T> Find()
        {
            return Find(null);
        }

        public virtual IQueryable<T> Find(System.Linq.Expressions.Expression<Func<T, bool>> predicate)
        {
            return Find(null, predicate);
        }

        public virtual IQueryable<T> Find(IEnumerable<string> includes, System.Linq.Expressions.Expression<Func<T, bool>> predicate)
        {
            IQueryable<T> query = _entities.Set<T>();

            foreach (var i in includes)
                query = query.Include(i);

            if (predicate != null)
                query = query.Where(predicate);

            return query;
        }

        public virtual void Add(T entity)
        {
            _entities.Set<T>().Add(entity);
        }

        public virtual void Delete(T entity)
        {
            var record = _entities.Set<T>().SingleOrDefault(r => r.GUID_RECORD == entity.GUID_RECORD);
            if (record == null)
                throw new Exception("Запись не найдена в базе");

            _entities.Set<T>().Remove(record);
        }

        public virtual void Edit(T entity)
        {
            _entities.Entry(entity).State = System.Data.EntityState.Modified;
        }

        public virtual void Save()
        {
            _entities.SaveChanges();
        }

        public virtual void InsertOrUpdate<R>(R record) where R : class, ICommonDbClass
        {
            if (record == null)
                return;

            if (record.GUID_RECORD == Guid.Empty)
                record.GUID_RECORD = Guid.NewGuid();

            if (_entities.Set<R>().Any(r => r.GUID_RECORD == record.GUID_RECORD))
            {
                try
                {
                    if (_entities.Entry(record).State == System.Data.EntityState.Modified)
                    {
                        _entities.Entry(record).CurrentValues.SetValues(record);
                    }

                    _entities.Entry(record).State = System.Data.EntityState.Modified;
                }
                catch
                {
                    /// В случае повторного обновления записи
                    /// возникает ошибка
                    /// нужно как-то обработать. Но как?
                }
            }
            else
            {
                _entities.Set<R>().Add(record as R);
            }
        }

        public virtual void InsertOrUpdate<R>(IEnumerable<R> records) where R : class, ICommonDbClass
        {
            if (records == null)
                return;

            records.ToList().ForEach(r => this.InsertOrUpdate<R>(r));
        }
    }
}